﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using WebService3.MerchantService;

namespace WebService3
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "GLKService" in code, svc and config file together.

	public class GLKService : IGLKService 
	{
		private IGLKServiceImplementation _service;
		public GLKService()
		{
			_service = ServiceFactory.CreateGlkService();
		}
 
        /// <summary>
        /// Отдает список продуктов
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Product> GetProducts()
        {
        	return _service.GetProducts();
        }

        /// <summary>
        /// Создает запись о заказе в БД сервиса
        /// Регистрирует заказ в системе электронных платежей
        /// Формирует набор данных для отображения на предпродажной странице
        /// </summary>
        /// <param name="userID">Идентификатор пользователя в БД сервиса</param>
        /// <param name="selectedProducts">Набор выбранных продуктов</param>
        /// <param name="returnUrl">Адрес, на который будет возвращен пользователь в стучае успешной оплаты</param>
        /// <returns>Набор данных для отображения на предпродажной странице или в случае ошибки NULL</returns>
        public OrderData GetPrePayData(int userID, IEnumerable<Product> selectedProducts, string returnUrl)
        {
            var db = new GLKDatabaseDataContext();
            try
            {
                // Создание записи в БД сервиса
            	IList<Product> products;
				glk_Order order = _service.CreateOrder(userID, selectedProducts, out products);

            	// Регистрация заказа
            	var mc = ServiceFactory.CreateMerchantService();
                var regResult = mc.Service.registerOrder(new orderParams { merchantOrderNumber = order.ID.ToString(), amount = Convert.ToInt32(Math.Round(products.Sum(x => x.Price) * 100)), returnUrl = returnUrl });
                mc.Client.Close();

                // Установка статуса заказа
                if (string.IsNullOrWhiteSpace(regResult.orderId))
                { throw new Exception(String.Format("Ошибка при регистрации заказа #{0}: {1} ", regResult.errorCode, regResult.errorMessage)); }

                _service.UpdateOrderState(order, regResult);

            	return new OrderData { Products = products, OrderID = regResult.orderId, OrderPrice = products.Sum(x => x.Price), FormUrl = regResult.formUrl };
            }
            catch (Exception ex)
            {
                WriteExceptionToLog(ex.Message);
                return null;
            }
            finally
            {
                db.Dispose();
            }
        }


    	/// <summary>
        /// Оболочка метода GetPrePayData для списка продуктов
        /// </summary>
        /// <param name="userID">Идентификатор пользователя в БД сервиса</param>
        /// <param name="products">Набор выбранных продуктов</param>
        /// <param name="returnUrl">Адрес, на который будет возвращен пользователь в стучае успешной оплаты</param>
        /// <returns>Набор данных для отображения на предпродажной странице или в случае ошибки NULL</returns>
        public OrderData GetPrePayDataProducts(int userID, IEnumerable<int> products, string returnUrl)
        {
            var selectedProducts = _service.GetProducts().Where(x => products.Contains(x.ID)).ToArray();
            return !selectedProducts.Any() ? null : GetPrePayData(userID, selectedProducts, returnUrl);
        }

        /// <summary>
        /// Оболочка метода GetPrePayData для пополнения баланса
        /// </summary>
        /// <param name="userID"></param>
        /// <param name="balance"></param>
        /// <param name="returnUrl"></param>
        /// <returns></returns>
        public OrderData GetPrePayDataBalance(int userID, int balance, string returnUrl)
        {
            return GetPrePayData(userID, new [] {_service.GetBalanceProduct()}, returnUrl);
        }

        /// <summary>
        /// Создает клиента в БД сервиса
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public int CreateClient(string email)
        {
			try
			{
				return _service.CreateClient(email);
			}
			catch(Exception ex)
			{
				WriteExceptionToLog(ex.Message);
				return 0;
			}
        }

        /// <summary>
        /// Получает идентификатор клиента по его элетронной почте
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public int GetClientByEmail(string email)
        {
            try
            {
            	return _service.GetClientByEmail(email);
            }
            catch (Exception ex)
            {
                WriteExceptionToLog(ex.Message);
                return 0;
            }
        }

        public PaymentResultData GetPaymentResult(string bankOrderID)
        {
            // Получить состояние из БД сервиса
            var orderInfo = _service.CheckOrderState(bankOrderID);
            
            if (orderInfo == null)
                return new PaymentResultData { OperationResult = false, OperationResultDescription = "Заказ не найден" };
            
            if (orderInfo.State == OrderState.Registered || orderInfo.State == OrderState.Declined)
            {
                // Получить подтверждение у банка 
                var bankResponse = GetBankConfirmation(bankOrderID);
                // Сохранить результат в БД сервиса
                _service.SavePaymentResult(bankResponse, ref orderInfo);
            }
            
            if (orderInfo.State == OrderState.Declined) 
                return new PaymentResultData { OperationResult = false, OperationResultDescription = "Банк не подтвердил оплату" };
            
            return orderInfo.State == OrderState.Confirmed && MarkOrderAsSoldInGLK(orderInfo)
                        ? new PaymentResultData { OperationResult = true, OperationResultDescription = "Заказ оплачен", Products = orderInfo.Products }
                        : new PaymentResultData { OperationResult = false, OperationResultDescription = "Заказ оплачен, но еще не обработан системой ГЛК" };
        }

        private orderStatusResponse GetBankConfirmation(string bankOrderID)
        {
            var mc = ServiceFactory.CreateMerchantService();
            try
            {
                var result = mc.Service.getOrderStatus(new orderStatusRequest {orderId = bankOrderID});
                mc.Client.Close();
                return result;
            }
            catch (Exception ex)
            {
                WriteExceptionToLog(ex.Message);
                return null;
            }
            finally
            {
                if (mc.Client.State != CommunicationState.Closed) mc.Client.Abort();
            }
        }

        /// <summary>
        /// Пометить заказ как проданный в ГЛК. Функция должна использоваться только после подтверждения заказа банком.
        /// </summary>
        /// <returns></returns>
        private bool MarkOrderAsSoldInGLK(OrderInfo orderInfo)
        {
            SendResultByEmail(orderInfo.ClientEmail, orderInfo.Products);
            return true;
        }

    	private void SendResultByEmail(string email, IEnumerable<Product> products)
        {
            var login = "glk_info";
            var pass = "glk_info2";
            MailMessage message = _service.GetMessage(email, products);

    		var client = new SmtpClient("smtp.mail.ru", 25) { Credentials = new NetworkCredential(login, pass) };
            try
            {
                client.Send(message);
            }
            catch (Exception ex)
            {
                WriteExceptionToLog(ex.Message);
            }
        }


    	/// <summary>
        /// Записывает сообщения в лог
        /// </summary>
        /// <param name="ex"></param>
        private void WriteExceptionToLog(string ex)
        {
            using (var sr = new StreamWriter("A://log.txt", true))
            {
                sr.WriteLine(DateTime.Now + " " + ex);
                sr.Close();
            }
        }
    }
}
